#include "vulkan/VKDevice.h"
#include "vulkan/VKTools.h"
void VKDevice::FindPhysicalDevice(VkInstance instance, VkSurfaceKHR surface)
{
	uint32_t phyDeviceCount = 0;
	vkEnumeratePhysicalDevices(instance, &phyDeviceCount, nullptr);
	std::vector<VkPhysicalDevice> phyDevices(phyDeviceCount);
	vkEnumeratePhysicalDevices(instance, &phyDeviceCount, phyDevices.data());

	SDL_Log("PHYSICAL DEVICE PROPERTIES:");
	for (const auto& phyDevice : phyDevices)
	{
		VkPhysicalDeviceProperties phyDeviceProperties;
		vkGetPhysicalDeviceProperties(phyDevice, &phyDeviceProperties);
		SDL_Log("DEVICE NAME:%s", phyDeviceProperties.deviceName);


		int tmpGraphicsFamilyIndex = -1;
		int tmpPresentFamilyIndex = -1;
		uint32_t queueFamilyCount = 0;
		vkGetPhysicalDeviceQueueFamilyProperties(phyDevice, &queueFamilyCount, nullptr);
		std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
		vkGetPhysicalDeviceQueueFamilyProperties(phyDevice, &queueFamilyCount, queueFamilies.data());

		for (uint32_t i = 0; i < queueFamilies.size(); ++i)
		{
			if (queueFamilies[i].queueCount > 0 && queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
				tmpGraphicsFamilyIndex = i;
			VkBool32 presentMode = VK_FALSE;
			vkGetPhysicalDeviceSurfaceSupportKHR(phyDevice, i, surface, &presentMode);
			if (presentMode)
				tmpPresentFamilyIndex = i;
			if (tmpGraphicsFamilyIndex >= 0 && tmpPresentFamilyIndex >= 0)
				break;
		}

		uint32_t extensionCount = 0;
		vkEnumerateDeviceExtensionProperties(phyDevice, nullptr, &extensionCount, nullptr);
		std::vector<VkExtensionProperties> availableExtensions(extensionCount);
		vkEnumerateDeviceExtensionProperties(phyDevice, nullptr, &extensionCount, availableExtensions.data());

		SDL_Log("Available Extensions:");
		for (auto avlExt : availableExtensions)
			SDL_Log("\t%s", avlExt.extensionName);

		uint32_t suitableExtensionCount = 0;
		for (auto avlExt : availableExtensions)
		{
			for (auto reqExt : VKTools::RequiredDeviceExtesions)
			{
				if (strcmp(avlExt.extensionName, reqExt) == 0)
				{
					suitableExtensionCount++;
					break;
				}
			}
		}

		VkSurfaceCapabilitiesKHR tmpSwapChainSurfaceCapabilities;
		std::vector<VkSurfaceFormatKHR> tmpSwapChainSurfaceFormats;
		std::vector<VkPresentModeKHR> tmpSwapChainPresentModes;

		vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phyDevice, surface, &tmpSwapChainSurfaceCapabilities);

		uint32_t surfaceFormatCount;
		vkGetPhysicalDeviceSurfaceFormatsKHR(phyDevice, surface, &surfaceFormatCount, nullptr);
		if (surfaceFormatCount != 0)
		{
			tmpSwapChainSurfaceFormats.resize(surfaceFormatCount);
			vkGetPhysicalDeviceSurfaceFormatsKHR(phyDevice, surface, &surfaceFormatCount, tmpSwapChainSurfaceFormats.data());
		}
		else
			SDL_Log("no available surface format");

		uint32_t presetnModeCount;
		vkGetPhysicalDeviceSurfacePresentModesKHR(phyDevice, surface, &presetnModeCount, nullptr);
		if (presetnModeCount != 0)
		{
			tmpSwapChainPresentModes.resize(presetnModeCount);
			vkGetPhysicalDeviceSurfacePresentModesKHR(phyDevice, surface, &presetnModeCount, tmpSwapChainPresentModes.data());
		}
		else SDL_Log("no available present mode");

		VkPhysicalDeviceFeatures supportedFeatures;
		vkGetPhysicalDeviceFeatures(phyDevice, &supportedFeatures);

		if (tmpGraphicsFamilyIndex >= 0 && tmpPresentFamilyIndex >= 0 && !tmpSwapChainSurfaceFormats.empty() && !tmpSwapChainPresentModes.empty() && suitableExtensionCount == VKTools::RequiredDeviceExtesions.size() && supportedFeatures.samplerAnisotropy)
		{
			m_PhysicalDevice = phyDevice;
			m_GraphicsQueueFamilyIndex = tmpGraphicsFamilyIndex;
			m_PresentQueueFamilyIndex = tmpPresentFamilyIndex;
			m_SwapChainSurfaceCapabilities = tmpSwapChainSurfaceCapabilities;
			m_SwapChainSurfaceFormats = tmpSwapChainSurfaceFormats;
			m_SwapChainPresentModes = tmpSwapChainPresentModes;
			break;
		}


	}
	if (m_PhysicalDevice == VK_NULL_HANDLE)
		SDL_Log("failed to find suitable GPU!");
}


void VKDevice::CreateLogicalDevice(VkSurfaceKHR surface, VKValidLayersAndExts* valLayersAndExt, bool isValidationLayersEnabled)
{
	std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
	std::set<int> queueFamilyIndices = { m_GraphicsQueueFamilyIndex,m_PresentQueueFamilyIndex };

	float queuePriority = 1.0f;
	for (int queueFamilyIndex : queueFamilyIndices)
	{
		VkDeviceQueueCreateInfo createInfo = {};
		createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
		createInfo.queueFamilyIndex = queueFamilyIndex;
		createInfo.queueCount = 1;
		createInfo.pQueuePriorities = &queuePriority;
		queueCreateInfos.emplace_back(createInfo);
	}

	VkPhysicalDeviceFeatures deviceFeatures = {};
	deviceFeatures.samplerAnisotropy = VK_TRUE;

	VkDeviceCreateInfo createInfo = {};
	createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
	createInfo.pQueueCreateInfos = queueCreateInfos.data();
	createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
	createInfo.pEnabledFeatures = &deviceFeatures;
	createInfo.enabledExtensionCount = static_cast<uint32_t>(VKTools::RequiredDeviceExtesions.size());
	createInfo.ppEnabledExtensionNames = VKTools::RequiredDeviceExtesions.data();
	if (isValidationLayersEnabled)
	{
		createInfo.enabledLayerCount = static_cast<uint32_t>(valLayersAndExt->m_RequiredValidationLayers.size());
		createInfo.ppEnabledLayerNames = valLayersAndExt->m_RequiredValidationLayers.data();
	}
	else
	{
		createInfo.enabledLayerCount = 0;
		createInfo.ppEnabledLayerNames = nullptr;
	}

	if (vkCreateDevice(m_PhysicalDevice, &createInfo, nullptr, &m_LogicDevice) != VK_SUCCESS)
		SDL_Log("failed to create logic device!");

	vkGetDeviceQueue(m_LogicDevice, m_GraphicsQueueFamilyIndex, 0, &m_GraphicsQueue);
	vkGetDeviceQueue(m_LogicDevice, m_PresentQueueFamilyIndex, 0, &m_PresentQueue);
}

void VKDevice::DestroyDevice()
{
	vkDestroyDevice(m_LogicDevice, nullptr);
}
